﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Dynamic;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Helpers;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;
using umbraco;
using umbraco.BusinessLogic;

namespace Creative.Website.Starter
{
    public static class MainHelper
    {
        #region Contact Forms

        /// <summary>
        /// Loads the html from a xml and a xslt
        /// </summary>
        /// <param name="xmlFilePath">The xml file path</param>
        /// <param name="xsltFilePath">the xslt file path</param>
        /// <returns>The formatted html</returns>
        public static string LoadHtml(this string xmlFilePath, string xsltFilePath)
        {
            // Load the XML doc and Xslt
            var doc = new XPathDocument(xmlFilePath);
            var xslt = new XslTransform();
            xslt.Load(xsltFilePath);

            // Get umbraco.library to work
            var xslArgs = new XsltArgumentList();
            xslArgs.AddExtensionObject("urn:umbraco.library", new library());

            string result;
            using (var ms = new MemoryStream())
            {
                using (var myWriter = new XmlTextWriter(ms, Encoding.UTF8))
                {
                    using (var rd = new StreamReader(ms))
                    {
                        // do the actual transform of XML
                        xslt.Transform(doc, xslArgs, myWriter);
                        ms.Position = 0;
                        result = rd.ReadToEnd();
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// Logs the information from the contact form
        /// </summary>
        /// <param name="filePath">The file path</param>
        /// <param name="strName">The name</param>
        /// <param name="strAddressLine1">The address line one</param>
        /// <param name="strAddressLine2">The address line two</param>
        /// <param name="strEmail">The email</param>
        /// <param name="strMessage">The message</param>
        public static void LogContactForm(this string filePath, string strName, string strAddressLine1, string strAddressLine2, string strEmail, string strMessage)
        {
            var now = DateTime.Now;
            var strTime = String.Format("{0:HH:mm:ss}", now);
            var strDate = String.Format("{0:dd/MM/yyyy}", now);

            // Check if XML file exists
            if (!File.Exists(filePath))
            {
                // XML file DOES not exist yet.
                // Create a new XmlTextWriter instance and set the XML writer to indent the XML as opposed to all one line.
                using (var writer = new XmlTextWriter(filePath, Encoding.UTF8) { Formatting = Formatting.Indented })
                {
                    writer.WriteStartDocument(); // start writing XML!
                    writer.WriteStartElement("logs"); // Create the <logs> root XML node
                    writer.WriteStartElement("log"); // Creating the <log> node
                    writer.WriteAttributeString("time", strTime); // Adding the time attribute to the <log> node. <log time="14:52:23">
                    writer.WriteAttributeString("date", strDate); // Adding the date attribute to the <log> node. <log time="14:52:23" date="22/02/2009">
                    writer.WriteStartElement("field"); // Creating the <field> node inside the <log> node
                    writer.WriteAttributeString("alias", "name"); // Add the alias attribute to the <field> node. <field alias="name">
                    writer.WriteString(strName); // Write value of the field node.
                    writer.WriteEndElement(); // Close the <field> node inside the <log> node
                    writer.WriteStartElement("field");  // Creating the <field> node inside the <log> node
                    writer.WriteAttributeString("alias", "addressLine1"); // Add the alias attribute to the <field> node. <field alias="addressLine1">
                    writer.WriteString(strAddressLine1); // Write value of the field node.
                    writer.WriteEndElement(); // Close the <field> node inside the <log> node
                    writer.WriteStartElement("field"); // Creating the <field> node inside the <log> node
                    writer.WriteAttributeString("alias", "addressLine2"); // Add the alias attribute to the <field> node. <field alias="addressLine2">
                    writer.WriteString(strAddressLine2); // Write value of the field node.
                    writer.WriteEndElement(); // Close the <field> node inside the <log> node
                    writer.WriteStartElement("field"); // Creating the <field> node inside the <log> node
                    writer.WriteAttributeString("alias", "email"); // Add the alias attribute to the <field> node. <field alias="email">
                    writer.WriteString(strEmail); // Write value of the field node.
                    writer.WriteEndElement(); // Close the <field> node inside the <log> node
                    writer.WriteStartElement("field"); // Creating the <field> node inside the <log> node
                    writer.WriteAttributeString("alias", "message"); // Add the alias attribute to the <field> node. <field alias="message">
                    writer.WriteCData(strMessage); // Write value of the field node, wrapped inside a CData tag
                    writer.WriteEndElement(); //Close the <field> node inside the <log> node
                    writer.WriteEndElement(); //Close the <log> node
                    writer.WriteEndDocument(); //Close the <logs> node
                }
            }
            else
            {
                // XML file already exists so lets add to the XML file.
                // Open up the XML file we already have.
                var xmlDoc = new XmlDocument();
                xmlDoc.Load(filePath);
                XmlElement logNode = xmlDoc.CreateElement("log"); // Create a new XML <log> node
                logNode.SetAttribute("time", strTime); // Adding the time attribute to the <log> node. <log time="14:52:23">
                logNode.SetAttribute("date", strDate); // Adding the date attribute to the <log> node. <log time="14:52:23" date="22/02/2009">
                XmlElement fieldNodeName = xmlDoc.CreateElement("field"); // Create a new xml <field> node
                fieldNodeName.SetAttribute("alias", "name"); // Add the alias attribute to the <field> node. <field alias="name">
                fieldNodeName.InnerText = strName; // Write value of the field node.
                logNode.AppendChild(fieldNodeName); // Add our <field> node to our <log> node
                XmlElement fieldNodeAddressLine1 = xmlDoc.CreateElement("field"); // Create a new xml <field> node
                fieldNodeAddressLine1.SetAttribute("alias", "addressLine1"); // Add the alias attribute to the <field> node. <field alias="addressLine1">
                fieldNodeAddressLine1.InnerText = strAddressLine1; // Write value of the field node.
                logNode.AppendChild(fieldNodeAddressLine1); // Add our <field> node to our <log> node
                XmlElement fieldNodeAddressLine2 = xmlDoc.CreateElement("field"); // Create a new xml <field> node
                fieldNodeAddressLine2.SetAttribute("alias", "addressLine2"); // Add the alias attribute to the <field> node. <field alias="addressLine2">
                fieldNodeAddressLine2.InnerText = strAddressLine2; // Write value of the field node.
                logNode.AppendChild(fieldNodeAddressLine2); // Add our <field> node to our <log> node
                XmlElement fieldNodeEmail = xmlDoc.CreateElement("field"); // Create a new xml <field> node
                fieldNodeEmail.SetAttribute("alias", "email"); // Add the alias attribute to the <field> node. <field alias="email">
                fieldNodeEmail.InnerText = strEmail; // Write value of the field node.
                logNode.AppendChild(fieldNodeEmail); // Add our <field> node to our <log> node
                XmlElement fieldNodeMessage = xmlDoc.CreateElement("field"); // Create a new xml <field> node
                fieldNodeMessage.SetAttribute("alias", "message"); // Add the alias attribute to the <field> node. <field alias="message">
                XmlCDataSection fieldNodeCData = xmlDoc.CreateCDataSection(strMessage); // Create a CDATA section that wraps the message text
                fieldNodeMessage.AppendChild(fieldNodeCData); // Add our cdata tag/node to our <field> node
                logNode.AppendChild(fieldNodeMessage); // Add our <field> node to our <log> node
                xmlDoc.SelectSingleNode("/logs").AppendChild(logNode); // Add our built up logNode that contains our <field> nodes to the main xml document
                xmlDoc.Save(filePath);
            }
        }

        public static void LogSendToFriendForm(this string filePath, string strUrl, string strFriendName, string strFriendEmail, string strYourName, string strYourEmail, string strMessage)
        {
            var now = DateTime.Now;
            var strTime = String.Format("{0:HH:mm:ss}", now);
            var strDate = String.Format("{0:dd/MM/yyyy}", now);

            if (!File.Exists(filePath))
            {
                using (var writer = new XmlTextWriter(filePath, Encoding.UTF8) { Formatting = Formatting.Indented })
                {
                    writer.WriteStartDocument();
                    writer.WriteStartElement("logs");
                    writer.WriteStartElement("log");
                    writer.WriteAttributeString("time", strTime);
                    writer.WriteAttributeString("date", strDate);
                    writer.WriteStartElement("field");
                    writer.WriteAttributeString("alias", "url");
                    writer.WriteString(strUrl);
                    writer.WriteEndElement();
                    writer.WriteStartElement("field");
                    writer.WriteAttributeString("alias", "friendName");
                    writer.WriteString(strFriendName);
                    writer.WriteEndElement();
                    writer.WriteStartElement("field");
                    writer.WriteAttributeString("alias", "friendEmail");
                    writer.WriteString(strFriendEmail);
                    writer.WriteEndElement();
                    writer.WriteStartElement("field");
                    writer.WriteAttributeString("alias", "yourName");
                    writer.WriteString(strYourName);
                    writer.WriteEndElement();
                    writer.WriteStartElement("field");
                    writer.WriteAttributeString("alias", "yourEmail");
                    writer.WriteString(strYourEmail);
                    writer.WriteEndElement();
                    writer.WriteStartElement("field");
                    writer.WriteAttributeString("alias", "message");
                    writer.WriteCData(strMessage);
                    writer.WriteEndElement();
                    writer.WriteEndElement();
                    writer.WriteEndDocument();
                }
            }
            else
            {
                var xmlDoc = new XmlDocument();
                xmlDoc.Load(filePath);
                XmlElement logNode = xmlDoc.CreateElement("log");
                logNode.SetAttribute("time", strTime);
                logNode.SetAttribute("date", strDate);
                XmlElement fieldNodeURL = xmlDoc.CreateElement("field");
                fieldNodeURL.SetAttribute("alias", "url");
                fieldNodeURL.InnerText = strUrl;
                logNode.AppendChild(fieldNodeURL);
                XmlElement fieldNodeFriendName = xmlDoc.CreateElement("field");
                fieldNodeFriendName.SetAttribute("alias", "friendName");
                fieldNodeFriendName.InnerText = strFriendName;
                logNode.AppendChild(fieldNodeFriendName);
                XmlElement fieldNodeFriendEmail = xmlDoc.CreateElement("field");
                fieldNodeFriendEmail.SetAttribute("alias", "friendEmail");
                fieldNodeFriendEmail.InnerText = strFriendEmail;
                logNode.AppendChild(fieldNodeFriendEmail);
                XmlElement fieldNodeYourName = xmlDoc.CreateElement("field");
                fieldNodeYourName.SetAttribute("alias", "yourName");
                fieldNodeYourName.InnerText = strYourName;
                logNode.AppendChild(fieldNodeYourName);
                XmlElement fieldNodeYourEmail = xmlDoc.CreateElement("field");
                fieldNodeYourEmail.SetAttribute("alias", "yourEmail");
                fieldNodeYourEmail.InnerText = strYourEmail;
                logNode.AppendChild(fieldNodeYourEmail);
                XmlElement fieldNodeMessage = xmlDoc.CreateElement("field");
                fieldNodeMessage.SetAttribute("alias", "message");
                XmlCDataSection fieldNodeCData = xmlDoc.CreateCDataSection(strMessage);
                fieldNodeMessage.AppendChild(fieldNodeCData);
                logNode.AppendChild(fieldNodeMessage);
                xmlDoc.SelectSingleNode("/logs").AppendChild(logNode);
                xmlDoc.Save(filePath);
            }
        }

        #endregion

        #region Email 

        /// <summary>
        /// Tries to send a mail
        /// </summary>
        /// <param name="recipients">Recipients email</param>
        /// <param name="subject">The subject</param>
        /// <param name="body">The content</param>
        /// <param name="nodeId">The node id (optional, if not set, defaults to -1)</param>
        /// <returns>True if email is sent; otherwise, false</returns>
        public static bool TrySendMail(string recipients, string subject, string body, int nodeId = -1)
        {
            using (var smtp = new SmtpClient())
            {
                var credentials = (NetworkCredential)smtp.Credentials;
                var from = credentials.UserName;
                try
                {
                    smtp.Send(@from, recipients, subject, body);
                    Log.Add(LogTypes.Custom, nodeId, "A new email has been sent from " + @from + " to " + recipients);
                    return true;
                }
                catch (Exception ex)
                {
                    Log.Add(LogTypes.Error, nodeId, String.Format("Message not sent! " + ex.Message + "|" + ex.StackTrace));
                    Log.Add(LogTypes.Error, nodeId, String.Format("Message contents: " + "from:" + @from + ";to:" + recipients + ";subject:" + subject + ";body:" + body));
                    return false;
                }
            }
        }

        #endregion

        #region Html

        public static string ConstructQueryString(this NameValueCollection parameters, string delimiter = "&", bool omitEmpty = true)
        {
            var items = new List<string>();
            for (var i = 0; i < parameters.Count; i++)
            {
                var strings = parameters.GetValues(i);
                if (strings == null)
                {
                    continue;
                }

                foreach (var value in strings)
                {
                    var addValue = !(omitEmpty) || !String.IsNullOrEmpty(value);
                    if (addValue)
                    {
                        items.Add(String.Concat(parameters.GetKey(i), '=', HttpUtility.UrlEncode(value)));
                    }
                }
            }

            return String.Join(delimiter, items.ToArray());
        }

        public static HtmlString GetAntiForgeryHtml(this HttpContextBase context)
        {
            return AntiForgery.GetHtml(context, String.Empty, String.Empty, String.Empty);
        }

        public static bool IsValidAntiForgery(this HttpContextBase context)
        {
            try
            {
                AntiForgery.Validate(context, String.Empty);
                return true;
            }
            catch
            {
                return false;
            }
        }

        public const string EmailRegex = @"^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$";

        public static bool IsEmail(this string email)
        {
            if (String.IsNullOrEmpty(email))
            {
                return false;
            }

            var re = new Regex(EmailRegex, RegexOptions.IgnoreCase);
            return re.IsMatch(email);
        }

        public static bool IsEmpty(this string value)
        {
            return String.IsNullOrEmpty(value);
        }

        public static dynamic ToDynamic(this NameValueCollection collection)
        {
            return new DynamicRequest(collection);
        }

        private class DynamicRequest : DynamicObject
        {
            private readonly NameValueCollection nvc;

            public DynamicRequest(NameValueCollection collection)
            {
                nvc = collection;
            }

            public override bool TryGetMember(GetMemberBinder binder, out object result)
            {
                result = nvc[binder.Name];
                return true;
            }
        }

        #endregion
    }
}
